import React, { Component } from 'react';
import { message, Cascader, Spin } from 'antd';
// import MapBody from './components/map-body';
import LeafletMap from './components/leaflet-map';
import VulPie from './components/vul-pie';
import SitesTrend from './components/sites-trend';
import Top10Risk from './components/top10-risk';
import Top10Vuls from './components/top10-vuls';
import TopNFinger from './components/top-n-finger';
import {
  getMapSiteStatics,
  getTop10RiskSites,
  getTopNFingers,
  getTop10Vuls,
  getSitesTrends,
  getAssetMapList
} from '@api/map';
import _ from 'lodash';
import InfoReview from './components/info-review';
import openNewPage from '@util/open-new-page';
import './index.less';
import './animation.less';

class AssetMap extends Component {
  constructor(props) {
    super(props);
    this.wrapperRef = React.createRef();
    this.state = {
      specificVul: {},
      totalSites: 0,
      totalVuls: 0,
      siteWithVuls: 0,
      availableSites: 0,
      sitesTrends: [],
      top10Risk: [],
      top10Vuls: [],
      top10Finger: {},
      fingerType: 'topAssetCategory',
      fingerLoading: false,
      vulsPieLoading: false,
      trendLoading: false,
      vulsLoading: false,
      topRiskLoading: false,
      locationFilter: {}, // { country, region, city }
      locationTree: [],
      points: [],
      locationFilterStatus: true
    };
    this.loadedLocationTree = false;
    this.preHtmlFontsize = window.document.documentElement.style.fontSize;
    this.throttledLoadAssetAndTree = _.debounce(this.loadAssetAndTree, 1000);
    this.throttledLoadData = _.debounce(this.loadData, 1000);
  }

  componentDidMount() {
    this.updateAssetMapFontsize();
    window.addEventListener('resize', this.updateAssetMapFontsize);
    this.throttledLoadData();
    this.loadAssetAndTree();
  }

  loadAssetAndTree = () => {
    const locationFilter = this.state.locationFilter;
    getAssetMapList(locationFilter).then(res => {
      let { locationTree = [], points = [] } = _.get(res, 'data.data', {});
      if (!this.loadedLocationTree) {
        this.setState(
          {
            locationTree,
            points
          },
          () => (this.loadedLocationTree = true)
        );
      } else {
        this.setState({
          points
        });
      }
    });
  };

  componentWillUnmount() {
    if (this.preHtmlFontsize) {
      window.document.documentElement.style.fontSize = this.preHtmlFontsize;
    } else {
      window.document.documentElement.style.removeProperty('font-size');
    }
    window.removeEventListener('resize', this.updateAssetMapFontsize);
  }

  updateAssetMapFontsize = () => {
    const scrollHeight = _.get(window, 'document.body.scrollHeight', 1080);
    const baseN = 100;
    const fontSize = scrollHeight / baseN;
    if (this.wrapperRef && this.wrapperRef.current) {
      this.wrapperRef.current.style.fontSize = `${fontSize}px`;
    }
    window.document.documentElement.style.fontSize = `${fontSize}px`;
  };

  loadData = () => {
    const locationFilter = this.state.locationFilter;
    this.setState({
      vulsPieLoading: true,
      trendLoading: true,
      vulsLoading: true,
      topRiskLoading: true
    });
    getMapSiteStatics(locationFilter)
      .then(res => {
        const sites = _.get(res, 'data.data.sites', 0);
        const availableSites = _.get(res, 'data.data.ips.available', 0);
        const siteWithVuls = _.get(res, 'data.data.site_with_vuls', 0);
        const totalVuls = _.sum(_.values(_.get(res, 'data.data.vuls', {})));
        const specificVul = _.get(res, 'data.data.specific_vuls_with_sites');
        this.setState({
          totalSites: sites,
          specificVul,
          totalVuls,
          siteWithVuls,
          availableSites,
          vulsPieLoading: false
        });
      })
      .catch(() => {
        this.setState({ vulsPieLoading: false });
        message.error('获取数据失败');
      });
    getTop10RiskSites(locationFilter)
      .then(res => {
        const top10Risk = _.get(res, 'data.data.list');
        this.setState({ top10Risk, topRiskLoading: false });
      })
      .catch(() => {
        this.setState({ topRiskLoading: false });
        message.error('获取数据失败');
      });
    getTop10Vuls(locationFilter)
      .then(res => {
        const top10Vuls = _.get(res, 'data.data.list');
        this.setState({ top10Vuls, vulsLoading: false });
      })
      .catch(() => {
        this.setState({ vulsLoading: false });
        message.error('获取数据失败');
      });
    getSitesTrends()
      .then(res => {
        const sitesTrends = _.get(res, 'data.data.trend');
        this.setState({ sitesTrends, trendLoading: false });
      })
      .catch(() => {
        this.setState({ trendLoading: false });
        message.error('获取数据失败');
      });
    this.loadFinger();
  };

  loadFinger = () => {
    const { fingerType, locationFilter, top10Finger = {} } = this.state;
    this.setState({ fingerLoading: true });
    getTopNFingers({ stepKey: fingerType, ...locationFilter, limit: 10 })
      .then(res => {
        const list = _.get(res, 'data.data.list', []);
        if (fingerType === 'port' && !_.isEmpty(list)) {
          // 保留端口中的0端口号使其正常显示, 并过滤不合规的值
          const port = list
            .filter(p => {
              return typeof p.name === 'number' && !isNaN(p.name);
            })
            .slice(0, 10)
            .map(p => {
              return {
                ...p,
                name: _.get(p, 'name', '').toString()
              };
            });
          top10Finger[fingerType] = port;
        } else {
          top10Finger[fingerType] = list.slice(0, 10);
        }

        this.setState({ top10Finger, fingerLoading: false });
      })
      .catch(() => {
        message.error('获取数据失败');
        this.setState({ fingerLoading: false });
      });
  };

  onTopNFingerChange = fingerType => {
    this.setState({ fingerType }, this.loadFinger);
  };

  onLocationChange = (values = []) => {
    let locationFilter = {
      country: values[0],
      region: values[1],
      city: values[2]
    };
    this.setState(
      {
        locationFilter,
        locationFilterStatus: !this.state.locationFilterStatus
      },
      () => {
        this.throttledLoadAssetAndTree();
        this.throttledLoadData();
      }
    );
  };

  getLocationFilter = () => {
    const { locationFilter } = this.state;
    const city = _.get(locationFilter, 'city', '');
    const region = _.get(locationFilter, 'region', '');
    const country = _.get(locationFilter, 'country', '');
    const filter = [
      city ? `exact:city="${city}"` : '',
      region ? `exact:region="${region}"` : '',
      country ? `exact:country="${country}"` : ''
    ]
      .filter(Boolean)
      .join(' && ');
    return filter;
  };

  riskJumpToAssetDetail = site => {
    const { url, ip, name } = site || {};
    const search = url
      ? `exact:url="${url}"`
      : ip
      ? `exact:ip="${ip}"`
      : name
      ? `exact:name="${name}"`
      : '';

    openNewPage(`/asset-manage?${encodeURIComponent(search)}`);
  };

  fingerJumpToAssetList = (e, key) => {
    const { top10Finger } = this.state;
    const name = _.get(top10Finger[key][e.dataIndex], 'name', '');

    const searchKey = key === 'topAssetCategory' ? 'assetCategory' : key;
    const search = (name ? [`exact:${searchKey}="${name}"`, this.getLocationFilter()] : [])
      .filter(Boolean)
      .join(' && ');

    if (search) {
      openNewPage(`/asset-manage?${encodeURIComponent(search)}`);
    }
  };

  vulPieJumpToAssetList = e => {
    const level = _.get(e, 'data.key', '');
    const search = [level ? `vulLevel="${level}"` : '', this.getLocationFilter()]
      .filter(Boolean)
      .join(' && ');

    openNewPage(`/asset-manage?${encodeURIComponent(search)}`);
  };

  topVulJumpToAssetList = data => {
    const name = _.get(data, 'name', '');
    const search = [name ? `exact:vul="${name}"` : '', this.getLocationFilter()]
      .filter(Boolean)
      .join(' && ');

    openNewPage(`/asset-manage?${encodeURIComponent(search)}`);
  };

  render() {
    const {
      totalSites,
      totalVuls,
      siteWithVuls,
      availableSites,
      top10Vuls,
      specificVul,
      sitesTrends,
      top10Risk,
      top10Finger,
      fingerLoading,
      vulsPieLoading,
      trendLoading,
      vulsLoading,
      topRiskLoading,
      locationTree,
      locationFilter,
      locationFilterStatus,
      points
    } = this.state;

    const locationCascaderValue = [
      locationFilter.country,
      locationFilter.region,
      locationFilter.city
    ].filter(Boolean);

    return (
      <div className="asset-map" ref={this.wrapperRef}>
        <div className="top">
          <a
            target="_blank"
            href="/asset-manage"
            rel="noopener noreferer"
            className="back-button"
            onClick={() => {
              sessionStorage.clear();
            }}
          >
            返回资产列表
          </a>
          <h1 className="title">地图资产大屏</h1>
        </div>
        <div className="info-review-wrapper">
          <InfoReview
            totalSites={totalSites}
            totalVuls={totalVuls}
            siteWithVuls={siteWithVuls}
            availableSites={availableSites}
          />
        </div>
        <div className="left">
          <div className="search">
            <Cascader
              value={locationCascaderValue}
              changeOnSelect
              options={locationTree}
              style={{ width: '100%' }}
              placeholder="请选择地区"
              onChange={this.onLocationChange}
              popupClassName={'asset-map-search-popup'}
              fieldNames={{ label: 'name', value: 'name', children: 'children' }}
            />
          </div>
          <div className="left-top">
            <div
              className="title"
              onClick={() => {
                sessionStorage.clear();
                openNewPage('/asset-manage');
              }}
            >
              资产变化趋势(全部资产)
            </div>
            <Spin spinning={trendLoading}>
              <SitesTrend data={sitesTrends} />
            </Spin>
          </div>
          <div className="left-bottom">
            <div className="title">Top 10 指纹分布</div>
            <Spin spinning={fingerLoading}>
              <TopNFinger
                data={top10Finger}
                totalSites={totalSites}
                items={[
                  { key: 'topAssetCategory', name: '资产分类(大类)' },
                  { key: 'assetCategory', name: '资产分类(细分)' },
                  { key: 'os', name: '操作系统' },
                  { key: 'port', name: '端口' },
                  { key: 'service', name: '服务' },
                  { key: 'component', name: '组件' },
                  { key: 'waf', name: '防护信息' }
                ]}
                onChange={this.onTopNFingerChange}
                jump={this.fingerJumpToAssetList}
              />
            </Spin>
          </div>
        </div>
        <div className="map">
          <LeafletMap
            points={points}
            locationFilter={locationFilter}
            locationFilterStatus={locationFilterStatus}
          />
        </div>
        <div className="right">
          <div className="right-top">
            <div className="title">漏洞等级占比</div>
            <Spin spinning={vulsPieLoading}>
              <VulPie
                data={specificVul}
                totalSites={totalSites}
                jump={this.vulPieJumpToAssetList}
              />
            </Spin>
          </div>
          <div className="right-bottom">
            <div className="title">Top 10 风险资产</div>
            <Spin spinning={topRiskLoading}>
              <Top10Risk data={top10Risk} jump={this.riskJumpToAssetDetail} />
            </Spin>
          </div>
        </div>
        <div className="bottom">
          <div className="top10-vuls-wrapper">
            <Spin spinning={vulsLoading}>
              <Top10Vuls data={top10Vuls} jump={this.topVulJumpToAssetList} />
            </Spin>
          </div>
        </div>
      </div>
    );
  }
}

export default AssetMap;
